package xaj.weavingtest.ast.istype;

import java.util.Hashtable;
import java.util.Set;

import polyglot.util.Position;
import abc.weaving.aspectinfo.Aspect;
import abc.weaving.aspectinfo.LocalPointcutVars;
import abc.weaving.aspectinfo.Pointcut;
import abc.weaving.aspectinfo.TargetAny;
import abc.weaving.aspectinfo.TargetType;
import abc.weaving.aspectinfo.TargetVar;
import abc.weaving.aspectinfo.Unification;
import abc.weaving.aspectinfo.Var;
import abc.weaving.aspectinfo.VarBox;
import abc.weaving.matching.WeavingEnv;
import abc.weaving.residues.Bind;
import abc.weaving.residues.ContextValue;
import abc.weaving.residues.Residue;
import abc.weaving.residues.WeavingVar;

public class TargetVar_isType extends TargetAny {
	protected Var var;
	protected WeavingVar weavingVar;

    public TargetVar_isType(Var var,Position pos) {
	super(pos);
	this.var = var;
    }

    /** Get the pointcut variable that is bound by this
     *  <code>target</code> pointcut.
     */
    public Var getVar() {
	return var;
    }

    public String toString() {
	return "target("+var+")";
    }

    /*
    protected Residue matchesAt(WeavingEnv we,ContextValue cv) {
		weavingVar = we.getWeavingVar(var);
		return Bind.construct
		    (cv,we.getAbcType(var).getSootType(),weavingVar);
    }
    */
    
    protected Residue matchesAt(WeavingEnv we,ContextValue cv) {
		weavingVar = we.getWeavingVar(var);
		
		System.out.println("em TARGETVAR: matching "+weavingVar.hashCode()+" with "+cv);
		MapValues.put(weavingVar, cv);
		
		return Bind.construct
		    (cv,we.getAbcType(var).getSootType(),weavingVar);
    }

    public Pointcut inline(Hashtable renameEnv,
			      Hashtable typeEnv,
			      Aspect context,
			      int cflowdepth) {
	Var var=this.var.rename(renameEnv);

	if(var==this.var) return this;
	else return new TargetVar_isType(var,getPosition());
    }

    public void getFreeVars(Set/*<String>*/ result) {
	result.add(var.getName());
    }

    /* (non-Javadoc)
	 * @see abc.weaving.aspectinfo.Pointcut#unify(abc.weaving.aspectinfo.Pointcut, java.util.Hashtable, java.util.Hashtable, abc.weaving.aspectinfo.Pointcut)
	 */
	public boolean unify(Pointcut otherpc, Unification unification) {

		if (otherpc.getClass() == this.getClass()) {
			Var othervar = ((TargetVar_isType)otherpc).getVar();
			if (var.unify(othervar, unification)) {
				Var unifiedvar = unification.getVar();
				if (unifiedvar == var) {
					unification.setPointcut(this);
					return true;
				} else {
					if (unification.unifyWithFirst())
						throw new RuntimeException("Unfication error: restricted unification failed");
					if (unifiedvar == othervar) {
					unification.setPointcut(otherpc);
					return true;
				} else {
					unification.setPointcut(new TargetVar_isType(unifiedvar, unifiedvar.getPosition()));
					return true;
				} 
				}
			} else return false;
		} else if (otherpc.getClass() == TargetType.class) {
			// If the other pc is a TargetType with the same type as this Var, can unify
			if (abc.main.Debug.v().debugPointcutUnification)
				System.out.println("Trying to unify a TargetVar_isType "+this+" with a TargetType: "+otherpc);
			TargetType othertt = (TargetType)otherpc;
			if (unification.getType1(this.getVar().getName()).equals(othertt.getType())) {
				if (abc.main.Debug.v().debugPointcutUnification)
					System.out.println("Succeeded!");
				unification.setPointcut(this);
				unification.put2(this.getVar(), new VarBox());
				return true;
			} else return false;
		} else // Do the right thing if otherpc was a local vars pc
			return LocalPointcutVars.unifyLocals(this,otherpc,unification);

	}
}
